#ifndef _COMPILATION_TASK_RENAMING_H_
#define _COMPILATION_TASK_RENAMING_H_

#include <map>

#include <compilation/task/Task.h>
#include <compilation/task/Handler.h>
#include <compilation/task/Propagator.h>

#include <compilation/instruction/Expression.h>
#include <compilation/instruction/ExpressionVisitor.h>


// RenamingTask and RenamingAgenda.
namespace compilation
{

class RenamingTask
    : public VirtualTask
{
public:

  typedef std::map<std::string,
  std::pair<ExpressionInstruction*, VirtualExpression*> > Apparitions;

private:

  Apparitions m_lastApparition;
  bool m_lValue;

public:

  bool isLValue(void) const
  { return m_lValue; }

  void setAsLValue(bool status)
  { m_lValue = status; }

  Apparitions const& getApparitions(void) const
  { return m_lastApparition; }

  virtual VirtualTask* clone(void) const
  { return new RenamingTask(*this); }

  ExpressionInstruction* getLastApparitionPosition(std::string const& name);

  VirtualExpression* getLastApparitionVariable(const std::string &name);

  void signalVariableModification(std::string const& name, ExpressionInstruction *where, VirtualExpression *what);

  RenamingTask(const Function& function)
    : m_lValue(false)
  { setInstruction(function.getFirstInstruction()); }

  RenamingTask(const RenamingTask& source) = default;

  virtual bool mergeWith(VirtualTask& source);

  friend class RenamingAgenda;
};


class RenamingAgenda
    : public VirtualAgenda
{
public:

  typedef RenamingTask Task;

  typedef InstructionVisitor<
  void, RenamingTask&, RenamingAgenda&, WorkListReusability&>
  instruction_handler_visitor_type;

  typedef ExpressionVisitor<
  void, RenamingTask&, RenamingAgenda&, WorkListReusability&>
  expression_handler_visitor_type;

  typedef InstructionVisitor<
  bool, RenamingTask&, RenamingAgenda&, WorkListReusability&>
  instruction_propagator_visitor_type;

  instruction_handler_visitor_type const&
  handleInstruction;

  expression_handler_visitor_type const&
  handleExpression;

  instruction_propagator_visitor_type const&
  propagateOnUnmarked;

private:

  std::list<LabelInstruction*> m_markedInstruction;
  std::list<ExpressionInstruction*> m_assignment;
  std::map<PhiExpression*, std::pair<bool,bool> > m_phiStatus;
  const Function* m_function;

public:

  RenamingAgenda(RenamingAgenda const& origin) = delete;

  RenamingAgenda(
      instruction_handler_visitor_type const& instruction_handler_visitor,
      expression_handler_visitor_type const& expression_handler_visitor,
      instruction_propagator_visitor_type const& instruction_propagator_visitor,
      Function const& function)
    : handleInstruction(instruction_handler_visitor)
    , handleExpression(expression_handler_visitor)
    , propagateOnUnmarked(instruction_propagator_visitor)
    , m_function(&function)
  { addNewAsFirst(new RenamingTask(function)); }

  virtual ~RenamingAgenda(void);

  std::pair<bool, bool>& getPhiStatus(PhiExpression* phi)
  {
    if (m_phiStatus.count(phi) == 0)
      return (m_phiStatus[phi] = std::make_pair(false,false));
    return m_phiStatus[phi];
  }

  void declareAssignmentMark(ExpressionInstruction* assign)
  { m_assignment.push_back(assign); }

  void markInstructionWith(
      VirtualInstruction &instruction, VirtualTask &task);
};

} // compilation

// RenamingExpressionHandler.
namespace compilation
{

template <ExpressionType _Type>
struct RenamingExpressionHandler
    : public ExpressionHandler<_Type, RenamingTask, RenamingAgenda>
{
  void operator ()(
      VirtualExpression* expr,
      RenamingTask& task,
      RenamingAgenda& continuations,
      WorkListReusability& reuse)
  {
    ExpressionHandler<_Type, RenamingTask, RenamingAgenda>
        ::operator ()(expr,task,continuations,reuse);
  }
};

template <>
struct RenamingExpressionHandler<TComparison>
    : public ExpressionHandler<TComparison, RenamingTask, RenamingAgenda>
{
  void operator ()(
      VirtualExpression* expr,
      RenamingTask& task,
      RenamingAgenda& continuations,
      WorkListReusability& reuse);
};

template <>
struct RenamingExpressionHandler<TUnaryOperator>
    : public ExpressionHandler<TUnaryOperator, RenamingTask, RenamingAgenda>
{
  void operator ()(
      VirtualExpression* expr,
      RenamingTask& task,
      RenamingAgenda& continuations,
      WorkListReusability& reuse);
};

template <>
struct RenamingExpressionHandler<TBinaryOperator>
    : public ExpressionHandler<TBinaryOperator, RenamingTask, RenamingAgenda>
{
  void operator ()(
      VirtualExpression* expr,
      RenamingTask& task,
      RenamingAgenda& continuations,
      WorkListReusability& reuse);
};

template <>
struct RenamingExpressionHandler<TDereference>
    : public ExpressionHandler<TDereference, RenamingTask, RenamingAgenda>
{
  void operator ()(
      VirtualExpression* expr,
      RenamingTask& task,
      RenamingAgenda& continuations,
      WorkListReusability& reuse);
};

template <>
struct RenamingExpressionHandler<TReference>
    : public ExpressionHandler<TReference, RenamingTask, RenamingAgenda>
{
  void operator ()(
      VirtualExpression* expr,
      RenamingTask& task,
      RenamingAgenda& continuations,
      WorkListReusability& reuse);
};

template <>
struct RenamingExpressionHandler<TCast>
    : public ExpressionHandler<TCast, RenamingTask, RenamingAgenda>
{
  void operator ()(
      VirtualExpression* expr,
      RenamingTask& task,
      RenamingAgenda& continuations,
      WorkListReusability& reuse);
};

template <>
struct RenamingExpressionHandler<TFunctionCall>
    : public ExpressionHandler<TFunctionCall, RenamingTask, RenamingAgenda>
{
  void operator ()(
      VirtualExpression* expr,
      RenamingTask& task,
      RenamingAgenda& continuations,
      WorkListReusability& reuse);
};

template <>
struct RenamingExpressionHandler<TAssign>
    : public ExpressionHandler<TAssign, RenamingTask, RenamingAgenda>
{
  void operator ()(
      VirtualExpression* expr,
      RenamingTask& task,
      RenamingAgenda& continuations,
      WorkListReusability& reuse);
};

template <>
struct RenamingExpressionHandler<TLocalVariable>
    : public ExpressionHandler<TLocalVariable, RenamingTask, RenamingAgenda>
{
  void operator ()(
      VirtualExpression* expr,
      RenamingTask& task,
      RenamingAgenda& continuations,
      WorkListReusability& reuse);
};

} // compilation

// RenamingInstructionHandler.
namespace compilation
{

template <InstructionType _Type>
struct RenamingInstructionHandler
    : public InstructionHandler<_Type, RenamingTask, RenamingAgenda>
{
  void operator ()(
      VirtualInstruction* instr,
      RenamingTask& task,
      RenamingAgenda& continuations,
      WorkListReusability& reuse)
  {
    InstructionHandler<_Type, RenamingTask, RenamingAgenda>
        ::operator ()(instr,task,continuations,reuse);
  }
};

template <>
struct RenamingInstructionHandler<TReturn>
    : public InstructionHandler<TReturn, RenamingTask, RenamingAgenda>
{
  void operator ()(
      VirtualInstruction* instr,
      RenamingTask& task,
      RenamingAgenda& continuations,
      WorkListReusability& reuse);
};

template <>
struct RenamingInstructionHandler<TExpression>
    : public InstructionHandler<TExpression, RenamingTask, RenamingAgenda>
{
  void operator ()(
      VirtualInstruction* instr,
      RenamingTask& task,
      RenamingAgenda& continuations,
      WorkListReusability& reuse);
};

template <>
struct RenamingInstructionHandler<TIf>
    : public InstructionHandler<TIf, RenamingTask, RenamingAgenda>
{
  void operator ()(
      VirtualInstruction* instr,
      RenamingTask& task,
      RenamingAgenda& continuations,
      WorkListReusability& reuse);
};

template <>
struct RenamingInstructionHandler<TGoto>
    : public InstructionHandler<TGoto, RenamingTask, RenamingAgenda>
{
  void operator ()(
      VirtualInstruction* instr,
      RenamingTask& task,
      RenamingAgenda& continuations,
      WorkListReusability& reuse);
};

template <>
struct RenamingInstructionHandler<TEnterBlock>
    : public InstructionHandler<TEnterBlock, RenamingTask, RenamingAgenda>
{
  void operator ()(
      VirtualInstruction* instr,
      RenamingTask& task,
      RenamingAgenda& continuations,
      WorkListReusability& reuse);
};

} // compilation

#endif // _COMPILATION_TASK_RENAMING_H_
